// Copyright 2009 Andy Kernahan
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Globalization;
using System.IO;
using AK.F1.Timing.Extensions;

namespace AK.F1.Timing.Live.Encryption
{
    /// <summary>
    /// An <see cref="AK.F1.Timing.Live.Encryption.IDecrypterFactory"/> implementation which
    /// creates decrypters seeded using an <see cref="AK.F1.Timing.AuthenticationToken"/>
    /// generated by the live-timing login service. This class cannot be inherited.
    /// </summary>
    public sealed class LiveDecrypterFactory : DecrypterFactoryBase
    {
        #region Private Fields.

        private const string SeedUrlFormat = "http://live-timing.formula1.com/reg/getkey/{0}.asp?auth={1}";

        #endregion

        #region Public Interface.

        /// <summary>
        /// Initialises a new instance of the <see cref="LiveDecrypterFactory"/> class and specifies
        /// the user's <see cref="AuthenticationToken"/>
        /// </summary>
        /// <param name="token">The user's <see cref="AuthenticationToken"/>.</param>
        /// <exception cref="System.ArgumentNullException">
        /// Thrown when <paramref name="token"/> is <see langword="null"/>.
        /// </exception>
        public LiveDecrypterFactory(AuthenticationToken token)
        {
            Guard.NotNull(token, "token");

            AuthToken = token.Token;
        }

        #endregion

        #region Protected Interface.

        /// <inheritdoc/>
        protected override int GetSeedForSession(string sessionId)
        {
            Guard.NotNullOrEmpty(sessionId, "sessionId");

            int seed;
            string response;
            Uri seedUri = MakeSeedUri(sessionId);

            Log.InfoFormat("fetching seed from {0}", seedUri);
            try
            {
                response = seedUri.GetResponseString(HttpMethod.Get);
            }
            catch(IOException exc)
            {
                Log.Error(exc);
                throw Guard.LiveDecrypterFactory_FailedToFetchSessionSeed(exc);
            }
            if(response.Equals("invalid", StringComparison.OrdinalIgnoreCase))
            {
                Log.Error("failed to fetch the seed as the authentication token has been rejected");
                throw Guard.LiveDecrypterFactory_AuthenticationTokenRejected();
            }
            if(!int.TryParse(response, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out seed))
            {
                Log.ErrorFormat("failed to parse seed from {0}", response);
                throw Guard.LiveDecrypterFactory_UnableToParseSeed(response);
            }

            return seed;
        }

        /// <inheritdoc/>
        protected override IDecrypter CreateWithSeed(int seed)
        {
            return new LiveDecrypter(seed);
        }

        #endregion

        #region Private Impl.

        private Uri MakeSeedUri(string sessionId)
        {
            return new Uri(string.Format(SeedUrlFormat, sessionId, AuthToken), UriKind.Absolute);
        }

        private string AuthToken { get; set; }

        #endregion
    }
}